/*
1.ll /dev/mycdev_simple0
crw------- 1 root root 236, 0 6月  12 20:40 /dev/mycdev_simple0

2.cat /proc/devices  查看设备
 devices[i] = *(device_create(mycdev_class, NULL, curr_devnum, NULL, MYCDEV_NAME "%d", i));

3./sys/class/mycdev_class/mycdev_simple0 ❯ ls                                       zlyx10@zlyx10-virtual-machine 20:46:09
dev  my_dev  power  subsystem  uevent
-rw------- 1 root root 4.0K 6月  12 20:45 my_dev

*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include "my_ioctl.h"

// 设备名称
#define MYCDEV_NAME "mycdev_simple"
// 设备类名称
#define MYCDEV_CLASS "mycdev_class"
// 设备数量
#define CDEV_NUM 1
// 缓冲长度
#define BUF_SIZE 1024

// 设备类
static struct class *mycdev_class;
// 所有设备的数组
static struct cdev cdevs[CDEV_NUM];
static struct device devices[CDEV_NUM];

// 设备号
static dev_t dev_num;
// 全局char数组缓冲
int buff_size = 1024;
static char *mybuf = NULL ;
static char mybuf_attr[BUF_SIZE];
// 反转标志位 1-反转 0-不反转
static int reverse_flag = 0;

static int strLength(const char *s);
static long mydev_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
static int mycdev_open(struct inode *inode, struct file *file);
static ssize_t mycdev_read(struct file *file, char __user *buff, size_t len, loff_t *pos);
static ssize_t mycdev_write(struct file *, const char __user *buff, size_t len, loff_t *pos);
static int mycdev_release(struct inode *inode, struct file *file);
static ssize_t mycdev_show(struct device *dev,
                           struct device_attribute *attr, char *buf);
static ssize_t mycdev_store(struct device *dev,
                            struct device_attribute *attr,
                            const char *buf, size_t len);
static void reverse_string(char *arr);
static DEVICE_ATTR(my_dev, S_IWUSR | S_IRUSR, mycdev_show, mycdev_store);
static struct file_operations op_f = {
    .read = mycdev_read,
    .write = mycdev_write,
    .open = mycdev_open,
    .release = mycdev_release,
    .unlocked_ioctl = mydev_ioctl,
};

// 模块初始化函数
static int __init simple_chardev_init(void)
{
    int i, retVal;
    /*设备号*/
    dev_t curr_devnum;

    /*请求内核分配设备号*/
    retVal = alloc_chrdev_region(&dev_num, 0, CDEV_NUM, MYCDEV_CLASS);
    if (retVal != 0)
    {
        printk("alloc_chrdev_region error %d\n", retVal);
        return retVal;
    }

    // 为字符设备创建设备类,在/sys/class中可见
    mycdev_class = class_create(THIS_MODULE, MYCDEV_CLASS);

    /*创建字符设备结构并注册到内核*/
    for (i = 0; i < CDEV_NUM; i++)
    {
        /*初始化字符设备*/
        cdev_init(&cdevs[i], &op_f);
        cdevs[i].owner = THIS_MODULE;

        /*设置设备号*/
        curr_devnum = MKDEV(MAJOR(dev_num), MINOR(dev_num) + i);
        /*添加设备到内核*/
        retVal = cdev_add(&cdevs[i], curr_devnum, 1);
        printk("cdev_add retVal %d\n", retVal);

        /*创建设备节点*/
        devices[i] = *(device_create(mycdev_class, NULL, curr_devnum, NULL, MYCDEV_NAME "%d", i));
        printk("device_create %p\n", &devices[i]);
        if (sysfs_create_file(&(devices[i].kobj), &dev_attr_my_dev.attr))
        { // 在mytest_device设备目录下创建一个my_device_test属性文件
            return -1;
        }
    }
    return retVal;
}

static void __exit simple_chardev_exit(void)
{
    dev_t _devnum;
    int i;

    for (i = 0; i < CDEV_NUM; i++)
    {
        _devnum = MKDEV(MAJOR(dev_num), MINOR(dev_num) + i);
        device_destroy(mycdev_class, _devnum);
        cdev_del(&(cdevs[i]));
    }
    class_destroy(mycdev_class);

    unregister_chrdev_region(dev_num, CDEV_NUM);
    printk("simple_chardev_exit\n");
    return;
}

static long mydev_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
    switch (cmd)
    {
    case DEV_REVERSE:
        printk("DEV_REVERSE");
        reverse_flag = 1;
        break;
    case DEV_UNREVERSE:
        printk("DEV_UNREVERSE");
        reverse_flag = 0;
        break;
    default:
        return -EINVAL;
    }
    return 0;
}

/*打开设备*/
static int mycdev_open(struct inode *inode, struct file *file)
{
    /*打印一条消息到内核日志*/
    printk("call mycdev_open\n");
    return 0;
}

/*读操作*/
static ssize_t mycdev_read(struct file *file, char __user *buff, size_t len, loff_t *pos)
{
    int count, retVal;
    printk("call mycdev_read\n");
    count = strLength(mybuf);
    if (reverse_flag == 1)
    {
        reverse_string(mybuf);
        retVal = copy_to_user(buff, mybuf, count + 1);
        reverse_string(mybuf);
        if (retVal == -EFAULT)
        {
            printk("mycdev_read error\n");
            return -EFAULT;
        }
        return count + 1;
    }
    retVal = copy_to_user(buff, mybuf, count + 1);
    if (retVal == -EFAULT)
    {
        printk("mycdev_read error\n");
        return -EFAULT;
    }
    return count + 1;
}

/*写操作*/
static ssize_t mycdev_write(struct file *file, const char __user *buff, size_t len, loff_t *pos)
{
    int retVal = 0;
    mybuf = (char *)kmalloc(buff_size * sizeof(char), GFP_KERNEL);
    if(len > buff_size){
        kfree(mybuf);
        mybuf = NULL;  //防止野指针
        buff_size = len;
        mybuf = (char *)kmalloc(buff_size * sizeof(char), GFP_KERNEL);
        if(mybuf == NULL){
            printk("kmalloc error\n");
            return -EFAULT;
        }
    }
    printk("call mycdev_write\n");
    retVal = copy_from_user(mybuf, buff, len);
    if (retVal == -EFAULT)
    {
        printk("mycdev_write error\n");
        return -EFAULT;
    }
    
    printk("recv %s\n", mybuf);
    return len;
}

/*SHOW函数*/
static ssize_t mycdev_show(struct device *dev,
                           struct device_attribute *attr, char *buf) // cat命令时,将会调用该函数
{
    int ret1 = 0;
    if (reverse_flag == 1)
    {
        reverse_string(mybuf_attr);
        ret1 = sprintf(buf, "%s\n", mybuf_attr);
        reverse_string(mybuf_attr);
        return ret1;
    }
    return sprintf(buf, "%s\n", mybuf_attr);
}
/*STORE函数*/
static ssize_t mycdev_store(struct device *dev,
                            struct device_attribute *attr,
                            const char *buf, size_t len) // echo命令时,将会调用该函数
{
    if (buf[0] == 'u')
    {
        reverse_flag = 1;
        printk("mycdev_store:unreverse");
    }
    else if (buf[0] == 'r')
    {
        reverse_flag = 0;
        printk("mycdev_store:reverse");
    }
    sprintf(mybuf_attr, "%s", buf);
    return len;
}

/*关闭文件*/
static int mycdev_release(struct inode *inode, struct file *file)
{
    printk("call mycdev_release\n");
    return 0;
}

/*计算字符串长度*/
static int strLength(const char *s)
{
    int len;
    len = 0;
    while ((*s) != 0)
    {
        s++;
        len++;
    }
    return len;
}

static void reverse_string(char *arr)
{
    char *left = arr;
    char *right = arr + strlen(arr) - 1;

    while (left < right)
    {
        char tmp = *left;
        *left = *right;
        *right = tmp;

        left++;
        right--;
    }
}

module_init(simple_chardev_init);
module_exit(simple_chardev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxxxxx");
MODULE_DESCRIPTION("内核描述");